package com.geek.bams.client;

import com.geek.bams.entity.Account;

import java.io.*;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @Desc 客户端网络通信服务类
 * @Author lirui
 * @Date 2025/10/22
 * 
 * 功能：封装客户端与服务器的网络通信逻辑
 */
public class ClientService {

    private Socket socket;
    private ObjectInputStream objectIn;
    private ObjectOutputStream objectOut;
    
    // 消息队列（用于存储从服务器接收的所有消息）
    private BlockingQueue<Object> messageQueue;
    
    // 刷新通知监听器
    private RefreshListener refreshListener;
    
    // 监听线程
    private Thread listenerThread;
    private volatile boolean listening = false;
    
    // 当前等待的响应（用于区分命令响应和推送消息）
    private volatile Object pendingResponse = null;
    
    /**
     * 刷新通知监听器接口
     */
    public interface RefreshListener {
        void onRefreshNeeded();
        void onForceLogout(String message);
        void onServerShutdown();
    }
    
    /**
     * 设置刷新监听器
     */
    public void setRefreshListener(RefreshListener listener) {
        this.refreshListener = listener;
    }
    
    /**
     * 连接到服务器
     * @return 是否连接成功
     */
    public boolean connect() {
        try {
            socket = new Socket("192.168.0.189", 8888);
            
            // 重要：必须先创建ObjectOutputStream并flush，确保头部信息被写入
            // 然后再创建ObjectInputStream，避免死锁
            objectOut = new ObjectOutputStream(socket.getOutputStream());
            objectOut.flush(); // 立即刷新，确保头部信息被写入
            objectIn = new ObjectInputStream(socket.getInputStream());
            
            // 初始化消息队列
            messageQueue = new LinkedBlockingQueue<>();
            
            // 启动监听线程
            startListenerThread();
            
            return true;
        } catch (IOException e) {
            System.err.println("连接服务器失败：" + e.getMessage());
            listening = false;
            return false;
        }
    }
    
    /**
     * 启动监听线程，持续接收服务器推送的消息
     */
    private void startListenerThread() {
        if (listenerThread != null && listenerThread.isAlive()) {
            return;
        }
        
        listening = true;
        listenerThread = new Thread(() -> {
            while (listening && socket != null && !socket.isClosed()) {
                try {
                    // 读取服务器发送的所有消息（包括命令响应和推送消息）
                    Object message = objectIn.readObject();
                    
                    // 放入消息队列
                    messageQueue.offer(message);
                    
                } catch (IOException e) {
                    if (listening) {
                        System.err.println("监听服务器消息时出错：" + e.getMessage());
                    }
                    break;
                } catch (ClassNotFoundException e) {
                    System.err.println("接收消息格式错误：" + e.getMessage());
                }
            }
        });
        listenerThread.setDaemon(true);
        listenerThread.start();
        
        // 启动消息处理线程
        startMessageProcessorThread();
    }
    
    /**
     * 启动消息处理线程，从队列中取出消息并处理
     */
    private void startMessageProcessorThread() {
        Thread processorThread = new Thread(() -> {
            while (listening) {
                try {
                    // 从队列中取出消息（阻塞等待）
                    Object message = messageQueue.poll(100, TimeUnit.MILLISECONDS);
                    if (message == null) {
                        continue;
                    }
                    
                    // 检查是否是特殊通知
                    if (message instanceof String) {
                        String msgStr = (String) message;
                        if ("REFRESH_ACCOUNT".equals(msgStr)) {
                            // 通知监听器刷新账户信息
                            if (refreshListener != null) {
                                refreshListener.onRefreshNeeded();
                            }
                            continue; // 不放入响应队列
                        } else if ("FORCE_LOGOUT".equals(msgStr)) {
                            // 强制登出通知
                            if (refreshListener != null) {
                                refreshListener.onForceLogout("该账户已在其他客户端登录！");
                            }
                            continue; // 不放入响应队列
                        } else if ("SERVER_SHUTDOWN".equals(msgStr)) {
                            // 服务器关闭通知
                            if (refreshListener != null) {
                                refreshListener.onServerShutdown();
                            }
                            // 断开连接
                            disconnect();
                            continue; // 不放入响应队列
                        }
                    }
                    // 这是命令响应，设置待处理响应
                    synchronized (this) {
                        pendingResponse = message;
                        notifyAll(); // 通知等待响应的线程
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        processorThread.setDaemon(true);
        processorThread.start();
    }
    
    /**
     * 断开与服务器的连接
     */
    public void disconnect() {
        listening = false;
        try {
            if (objectIn != null) {
                objectIn.close();
            }
            if (objectOut != null) {
                objectOut.close();
            }
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        } catch (IOException e) {
            System.err.println("断开连接时出错：" + e.getMessage());
        }
    }
    
    /**
     * 发送命令并接收响应
     * @param command 命令字符串
     * @return 服务器返回的对象（Account或String）
     */
    public Object sendCommand(String command) {
        // 检查连接状态
        if (socket == null || socket.isClosed() || !listening) {
            if (!connect()) {
                return "无法连接到服务器，请确保服务器已启动！";
            }
        }
        
        try {
            synchronized (this) {
                // 再次检查连接状态
                if (socket == null || socket.isClosed() || !listening) {
                    return "服务器连接已断开，无法执行操作！";
                }
                
                // 清空待处理的响应
                pendingResponse = null;
                
                // 使用ObjectOutputStream发送命令字符串，避免流冲突
                objectOut.writeObject(command);
                objectOut.flush();
                
                // 等待响应（最多等待5秒）
                long startTime = System.currentTimeMillis();
                while (pendingResponse == null && (System.currentTimeMillis() - startTime) < 5000) {
                    wait(100);
                }
                
                if (pendingResponse != null) {
                    Object result = pendingResponse;
                    pendingResponse = null;
                    return result;
                } else {
                    return "等待服务器响应超时";
                }
            }
            
        } catch (InterruptedException e) {
            return "网络通信被中断：" + e.getMessage();
        } catch (Exception e) {
            return "网络通信错误：" + e.getMessage();
        }
    }
    
    /**
     * 登录
     * @param id 账户ID
     * @param password 密码
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object login(Long id, String password) {
        String command = "login#" + id + "#" + password;
        return sendCommand(command);
    }
    
    /**
     * 注册
     * @param password 密码
     * @param repassword 确认密码
     * @param name 姓名
     * @param personID 身份证号
     * @param email 邮箱
     * @param type 账户类型
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object register(String password, String repassword, String name, 
                          String personID, String email, int type) {
        String command = "register#" + password + "#" + repassword + "#" + name + 
                        "#" + personID + "#" + email + "#" + type;
        return sendCommand(command);
    }
    
    /**
     * 存款
     * @param id 账户ID
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object deposit(Long id, double money) {
        String command = "deposit#" + id + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 取款
     * @param id 账户ID
     * @param password 密码
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object withdraw(Long id, String password, double money) {
        String command = "withdraw#" + id + "#" + password + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 转账
     * @param fromId 转出账户ID
     * @param password 密码
     * @param toId 转入账户ID
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object transfer(Long fromId, String password, Long toId, double money) {
        String command = "transfer#" + fromId + "#" + password + "#" + toId + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 贷款
     * @param id 账户ID
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object requestLoan(Long id, double money) {
        String command = "requestLoan#" + id + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 还款
     * @param id 账户ID
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object payLoan(Long id, double money) {
        String command = "payLoan#" + id + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 设置透支额度
     * @param id 账户ID
     * @param password 密码
     * @param money 金额
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object updateCeiling(Long id, String password, double money) {
        String command = "updateCeiling#" + id + "#" + password + "#" + money;
        return sendCommand(command);
    }
    
    /**
     * 获取账户信息
     * @param id 账户ID
     * @param password 密码
     * @return Account对象（成功）或错误信息字符串（失败）
     */
    public Object getAccount(Long id, String password) {
        String command = "getAccount#" + id + "#" + password;
        return sendCommand(command);
    }
    
    /**
     * 检查是否已连接到服务器
     * @return 是否已连接
     */
    public boolean isConnected() {
        return socket != null && !socket.isClosed() && listening;
    }
}

